package com.yf.modules.security.configuration;

import com.yf.modules.security.handler.JwtAccessDeniedHandler;
import com.yf.modules.security.handler.JwtAuthenticationEntryPoint;
import com.yf.modules.security.handler.JwtFilter;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;

/**
 * @author chentianwei
 * @date 2023/4/24 10:50
 * @description 安全设置
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
    private final JwtFilter jwtFilter;
    private final CorsFilter corsFilter;
    private final UserDetailsService userDetailsService;

    public SecurityConfig(
            JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
            JwtAccessDeniedHandler jwtAccessDeniedHandler,
            JwtFilter jwtFilter,
            CorsFilter corsFilter,
            UserDetailsService userDetailsService) {

        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
        this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
        this.jwtFilter = jwtFilter;
        this.corsFilter = corsFilter;
        this.userDetailsService = userDetailsService;
    }

    private static final String[] AUTH_LIST = {
            "/error",
            "/v3/api-docs/**",
            "/api-docs/**",
            "/*/*.html",
            "/swagger-resources/**",
            "/swagger-ui/**",
            "/v3/api-docs/**",
            "/swagger-ui.html",
            "/auth/login",
            "/auth/logout"
    };

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

        httpSecurity.
                //跨域请求会先进行一次options请求
                authorizeHttpRequests().
                requestMatchers(HttpMethod.OPTIONS).permitAll().and().
                //放行自定义列表
                authorizeHttpRequests().
                requestMatchers(AUTH_LIST).permitAll().and().
                //放行静态资源
                authorizeHttpRequests().
                requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll().and().
                //除了白名单列表，全部要鉴权
                authorizeHttpRequests().anyRequest().authenticated().and().
                //关闭iframe保护
                headers().frameOptions().disable().and().
                //关闭CSRF保护
                csrf().disable().
                //不使用缓存session
                sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                //异常处理器
                .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class).
                exceptionHandling().
                accessDeniedHandler(jwtAccessDeniedHandler).
                authenticationEntryPoint(jwtAuthenticationEntryPoint).and().
                //添加JWT过滤器
                addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);

        return httpSecurity.build();
    }

    /**
     * 注入Bcrypt加密器
     *
     * @return BCryptPasswordEncoder 加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 改用自定义的UserDetailsService
     */
    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity security) throws Exception {
        return security.getSharedObject(AuthenticationManagerBuilder.class).
                userDetailsService(userDetailsService).and().build();
    }
}
